#-*- coding: utf-8-*-

##  easygconf - a dict-like API for GConf
##               <http://www.florian-diesch.de/software/easygconf/>
##  Copyright (C) 2008, 2009 Florian Diesch <devel@florian-diesch.de>
##  
##  This program is free software; you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation; either version 2 of the License, or
##  (at your option) any later version.
##  
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##  
##  You should have received a copy of the GNU General Public License along
##  with this program; if not, write to the Free Software Foundation, Inc.,
##  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import gconf


class GConfDict(object):
    """
    A dict-like interface to GConf
    """

    def __init__(self, dir=None):
        """
         Arguments:

         - `dir`: The base dir for this GConfDict.
                  Any key not starting with '/' is realtiv to this.
        """
        if dir is None:
            dir = ''
        
        self.dir = dir.rstrip('/')
        self.gclient = gconf.client_get_default()
        if self.dir != '':
            self.gclient.add_dir(self.dir.rstrip('/'), 
                                 gconf.CLIENT_PRELOAD_NONE)
            

    def normalize_key(self, key):
        """
        Expands relativ keys if needed
        """
        if len(key) > 0 and key[0] != '/':
            key='%s/%s'%(self.dir, key)
        return key
    
    def normalize_dir(self, dir):
        """
        Expands relativ dirs if needed
        """
        if dir == '':
            return '/'
        else:
            return dir.rstrip('/')

        
    def get(self, key):
        """
        Returns the value for `key`
        """
        return self.to_python(self.gclient.get(self.normalize_key(key)))

    def set(self, key, value):
        """
        Set value for `key` to `value`
        """
        self.gclient.set(self.normalize_key(key), self.from_python(value))
            

    def unset(self, key):
        """
        Unset `key`
        """
        key = self.normalize_key(key)
        self.gclient.unset(key)

    def sync(self):
        """
        Suggests to gconfd that you've just finished a block of
        changes, and it would be an optimal time to sync to permanent
        storage. This is only a suggestion; and gconfd will eventually
        sync even if you don't call sync(). This
        function is just a "hint" provided to gconfd to maximize
        efficiency and minimize data loss.
        """
        self.gclient.suggest_sync()
    
        
    def add_listener(self, key, func, *args):
        """
        Request notification of changes to key

        - `func`: function to call when changes occur.
                  It's called as func(key, value, gconfdict, id, args)
        - `args`: user data to pass to func

        The function returns a connection ID you can use to call
        remove_listener()
        """
        key = self.normalize_key(key)

        def foo(client, id, entry, *args):
            func(entry.key, self.to_python(entry.value), self, id, *args)

        return self.gclient.notify_add(key, foo, args)

    def remove_listener(self, id):
        """
        Remove a notification using the ID returned from add_listener()
        
        - `id`: connection ID returned from add_listener()
        """
        self.gclient.notify_remove(id)

    def has_key(self, key):
        """
        True if `key` is defined and not a dir
        """
        return self.normalize_key(key) in self.keys()

    
    def to_python(self, gcvalue):
        """
        Convert a gconf value to a python value
        Arguments:
        - `gcvalue`: a gconf value
        """
        if gcvalue is None:
            return None
        else:
            _type=gcvalue.type
            if _type is gconf.VALUE_LIST:
                return tuple(self.to_python(v) for v in gcvalue.get_list())
            elif _type is gconf.VALUE_PAIR:
                return self.to_python(gcvalue.get_car()), self.to_python(gcvalue.get_cdr())
            else:
                return getattr(gcvalue, 'get_%s'%_type.value_nick)()


    def from_python(self, value):
        """
        Convert a Python value to a gconf value
        """
        if isinstance(value, basestring):
            val=gconf.Value(gconf.VALUE_STRING)
            val.set_string(value)
        elif isinstance(value, int) or  isinstance(value, long):
            val=gconf.Value(gconf.VALUE_INT)
            val.set_int(value)
        elif isinstance(value, float):
            val=gconf.Value(gconf.VALUE_FLOAT)
            val.set_float(value)
        elif isinstance(value, bool):
            val=gconf.Value(gconf.VALUE_BOOL)
            val.set_bool(value)
        elif isinstance(value, tuple) and len(value)==2:
            val=gconf.Value(gconf.VALUE_PAIR)
            val.set_car(self.from_python(value[0]))
            val.set_cdr(self.from_python(value[1]))
        elif isinstance(value, list) or  isinstance(value, tuple):
            val=gconf.Value(gconf.VALUE_LIST)
            l=[self.from_python(v) for v in value]
            if len(l) > 0:
                val.set_list_type(l[0].type)
            val.set_list(l)
        else: raise ValueError()
        return val

    def iterkeys(self):
        """
        return an iterator over the keys
        """
        for v in self.gclient.all_entries(self.normalize_dir(self.dir)):
            yield v.key
        
    def keys(self):
        """
        return a list of all keys
        """
        return tuple(self.iterkeys())


    def itervalues(self):
        """
        return an iterator over the values
        """
        for v in self.gclient.all_entries(self.dir):
            yield self.to_python(v.value())

    def values(self):
        """
        return a list of all values
        """
        return tuple(self.itervalues())

    def iteritems(self):
        """
        return an iterator over (key, value) pairs
        """
        for v in self.gclient.all_entries(self.dir):
            yield v.key, self.to_python(v.value)

             
    def iterdirs(self):
        """
        return an iterator over the subdirs
        """
        for d in self.gclient.all_dirs(self.dir):
            yield d

    def dirs(self):
        """
        return a list of all subdirs
        """  
        return tuple(self.iterdirs())
    
        
    def children(self):
        """
        return a list of all sudbirs and keys
        """
        return self.dirs()+self.keys()

    
    def __getitem__(self, key):
        """
        x.__getitem__(y) <==> x[y]
        """
        return self.get(key)

    def __setitem__(self, key, value):
        """
        x.__setitem__(i, y) <==> x[i]=y
        """
        self.set(key, value)

    def  __iter__(self):
        """
        x.__iter__() <==> iter(x)
        """
        return self.iterkeys()

    def __delitem__(self, key):
        """
        x.__delitem__(y) <==> del x[y]
        """
        self.delete(key)

    def __len__(self):
        """
        x.__len__() <==> len(x)
        """
        return len(self.keys())    

    def __contains__(self, item):
        """
        x.__contains__(y) <==> y in x
        """
        return self.has_key(item)
        

